import React, { Component, createRef, ReactElement } from 'react';
import { Button, message, Table } from 'antd';
import EditableCell, { editData } from './EditableCell';
import EditableRow, { IformRef, validateMessages } from './EditableFormRow';
import { SortableContainer } from 'react-sortable-hoc';
import { arrayMoveImmutable } from 'array-move';
import { InbizIcon, uuid } from '@inbiz/utils';
import './index.less';
import Color from '../Color';
import { validateRules } from 'rc-field-form/lib/utils/validateUtil';

type TableProps = Parameters<typeof Table>[0];

interface EditableTableState {
  dataSource: any[];
  columns: (ColumnTypes[number] & {
    editData?: editData;
    dataIndex: string;
    isIdentical?: boolean;
  })[];
}

type ColumnTypes = Exclude<TableProps['columns'], undefined>;

type editableRef = {
  getData: () => object[];
};
type EditableTableProps = Omit<TableProps, 'ref' | 'title'> & {
  title?: string | ReactElement;
  titlePosition?: 'before' | 'after';
  dataSource: object[];
  columns: (ColumnTypes[number] & {
    editData?: editData;
    dataIndex: string;
    isIdentical?: boolean;
  })[];
  showAdd?: boolean;
  addText?: string;
  onAdd?: (record: object[]) => object;
  isRowSelection?: boolean;
  addBtn:
    | boolean
    | {
        children: string;
        className?: string;
        type?: 'primary' | 'ghost' | 'dashed' | 'link' | 'text' | 'default';
        icon?: ReactElement;
        [key: string]: any;
      };
  deleteBtn:
    | boolean
    | {
        children: string;
        className?: string;
        type?: 'primary' | 'ghost' | 'dashed' | 'link' | 'text' | 'default';
        icon?: ReactElement;
        danger?: boolean;
        [key: string]: any;
      };
  ref?: any;
  maxLength?: number;
  adddisabled?: boolean;
};
const Sortable = SortableContainer((props: any) => <tbody {...props} />);

class EditableTable extends Component<EditableTableProps, EditableTableState> {
  static defaultProps: {
    bordered: boolean;
    size: string;
    pagination: boolean;
    dataSource: any[];
    addBtn: boolean;
    deleteBtn: boolean;
    titlePosition: 'after' | 'before';
    isRowSelection?: boolean;
  };
  protected checkedKeys: string[] = [];
  protected formRef: { [key: string]: IformRef } = {};

  constructor(props: EditableTableProps) {
    super(props);
    this.state = {
      dataSource: (props.dataSource || []).map((item) => {
        const newItem = { ...item };
        const rowKey =
          typeof props.rowKey === 'function'
            ? props.rowKey(item)
            : props.rowKey || 'id';
        if (!newItem[rowKey]) {
          newItem[rowKey] = uuid(0, 15);
        }
        return newItem;
      }),
      columns: this.columnsFormat(props.columns),
    };
    if (
      this.props.maxLength &&
      this.state.dataSource.length > this.props.maxLength
    ) {
      this.state.dataSource.length = this.props.maxLength;
    }
  }

  columnsFormat = (columns: EditableTableProps['columns']) => {
    return (columns || []).map((col: EditableTableProps['columns'][number]) => {
      if (!col.editData) {
        return col;
      }
      // 验证输入空格不通过
      let editData = col.editData;
      if (editData?.rules) {
        const requiredData = editData.rules.find((item) => item.required);
        const validator = editData.rules.find((item) => item.validator);
        if (requiredData?.required && !validator) {
          editData?.rules.push({
            validator: (rule, value: string) => {
              if (typeof value === 'string') {
                return value.length === 0 || value.trim().length
                  ? Promise.resolve()
                  : Promise.reject();
              } else {
                return Promise.resolve();
              }
            },
            message: requiredData.message || validateMessages.required,
          });
        }
      }
      const renderMap = {
        Color: (value: string) => <Color value={value} />,
        IconPicker: (value: string) => <InbizIcon type={value} />,
        Select: (value: any) => {
          return col.editData?.options?.find((item) => item.value === value)
            ?.label;
        },
      };
      return {
        render: renderMap[col.editData?.type],
        ...col,
        onCell: (record: any, rowIndex) => ({
          record,
          dataIndex: col.dataIndex,
          rowIndex: rowIndex,
          title: col.title,
          editData,
          handleSave: this.handleSave,
        }),
      };
    }) as EditableTableProps['columns'];
  };
  /*** 对外暴露的接口 */
  getData = async () => {
    await this.valid();
    return this.state.dataSource;
  };
  valid = async () => {
    if (
      this.props.maxLength &&
      this.state.dataSource.length > this.props.maxLength
    ) {
      await Promise.reject({
        type: 'maxLength',
        value: this.props.maxLength,
        msg: `表格数据超过最大限制"${this.props.maxLength}条"`,
      });
    }
    const { columns, dataSource } = this.state;
    const validColumns = columns.filter((item) => item?.editData?.rules);

    if (dataSource?.length && validColumns.length) {
      for (const data of dataSource) {
        for (const column of validColumns) {
          await validateRules(
            [column['dataIndex']],
            data[column['dataIndex']],
            column.editData?.rules,
            {},
            true,
          ).catch((e) => {
            const ref = this.formRef[data[this.getKey()]];
            ref.refs[column['dataIndex']]?.current?.startEdit();
            ref.validateFields();
            return Promise.reject(e);
          });
        }
      }
    }
    const columnsIdenticalList = (
      this.props.columns as {
        dataIndex: string;
        title: string;
        isIdentical?: boolean;
      }[]
    ).filter((item) => item.isIdentical);
    if (columnsIdenticalList?.length) {
      //同列字段重名效验
      let titleMsg;
      const isIdenticalResult = columnsIdenticalList.every((item) => {
        const { dataIndex, title } = item;
        const list = this.state.dataSource
          .map((item) => item[dataIndex])
          .filter((item) => item !== '' && item !== undefined && item !== null);
        if (list.length !== [...new Set(list)].length) {
          titleMsg = dataIndex === 'operationflag' ? '标识' : title;
          return false;
        } else {
          return true;
        }
      });
      if (!isIdenticalResult) {
        message.error(<span>{titleMsg}不能重复!</span>);
        await Promise.reject({
          type: 'identical',
          msg: `${titleMsg}不能重复!`,
        });
      }
    }
  };
  // 外部更新数据
  updateSource = (id: string, data: object) => {
    const index = this.state.dataSource.findIndex(
      (item) => item[this.getKey()] === id,
    );
    if (index !== -1) {
      const newData = [...this.state.dataSource];
      newData[index] = Object.assign({}, this.state.dataSource[index], data);
      this.setState({
        dataSource: newData,
      });
      return true;
    } else {
      return false;
    }
  };

  /*** 对外暴露的接口 */

  componentDidUpdate(prevProps: EditableTableProps) {
    if (prevProps.dataSource !== this.props.dataSource) {
      this.setState({ dataSource: this.props.dataSource });
    }
    if (prevProps.columns !== this.props.columns) {
      this.setState({ columns: this.columnsFormat(this.props.columns) });
    }
  }

  onSortEnd = ({
    oldIndex,
    newIndex,
  }: {
    oldIndex: number;
    newIndex: number;
  }) => {
    const { dataSource } = this.state;
    if (oldIndex !== newIndex) {
      const newData = arrayMoveImmutable(
        [...dataSource],
        oldIndex,
        newIndex,
      ).filter((el) => !!el);
      this.setState({ dataSource: newData });
    }
  };

  DraggableContainer = (props: any) => {
    const sortableRef: any = createRef();
    return (
      <Sortable
        pressDelay={200}
        helperClass="row-dragging"
        onSortEnd={this.onSortEnd}
        helperContainer={() => {
          return sortableRef.current?.container || document.body;
        }}
        ref={sortableRef}
        {...props}
      />
    );
  };

  handleDelete = () => {
    if (!this.checkedKeys.length) {
      message.warning('请选择需要删除的列');
    } else {
      this.setState({
        dataSource: this.state.dataSource.filter(
          (item) => !this.checkedKeys.includes(item[this.getKey()]),
        ),
      });
      this.checkedKeys = [];
    }
  };

  handleAdd = () => {
    const { dataSource } = this.state;
    const newData = this.props.onAdd?.(dataSource) || {};
    this.setState({
      dataSource: [...dataSource, newData],
    });
  };
  handleSave = (row: any) => {
    const newData = [...this.state.dataSource];
    const key = this.getKey(row);
    const index = newData.findIndex(
      (item) => row[key as string] === item[key as string],
    );

    const item = newData[index];
    newData.splice(index, 1, {
      ...item,
      ...row,
    });
    this.setState({ dataSource: newData });
  };

  getKey = (row?: any): string => {
    let key = this.props.rowKey || 'key';
    if (typeof key === 'function') {
      key = key(row || this.state.dataSource[0]) as string;
    }
    return key;
  };
  getTitle = () => {
    if (this.props.title || this.props.addBtn || this.props.deleteBtn) {
      const addBtn =
        this.props.addBtn &&
        (typeof this.props.addBtn === 'boolean'
          ? { children: '添加', type: 'primary' as const }
          : this.props.addBtn);
      const deleteBtn =
        this.props.deleteBtn &&
        (typeof this.props.deleteBtn === 'boolean'
          ? { children: '删除', danger: true }
          : this.props.deleteBtn);
      return (
        <>
          {this.props.title &&
            this.props.titlePosition === 'before' &&
            this.props.title}
          {addBtn && (
            <Button
              {...addBtn}
              disabled={
                this.props.adddisabled ? this.props.adddisabled : undefined
              }
              onClick={this.handleAdd}
            />
          )}
          {deleteBtn && (
            <Button
              {...deleteBtn}
              disabled={!(this.state.dataSource.length > 0)}
              onClick={this.handleDelete}
            />
          )}
          {this.props.title &&
            this.props.titlePosition === 'after' &&
            this.props.title}
        </>
      );
    } else {
      return undefined;
    }
  };
  handleCheckChange = (keys: string[], rows: object[]) => {
    this.checkedKeys = keys;
    this.props.rowSelection?.onChange?.(keys, rows);
  };

  render() {
    const { dataSource, columns } = this.state;
    const rowKey = this.getKey(dataSource[0]);
    this.formRef = {};
    const components = {
      body: {
        wrapper: this.DraggableContainer,
        row: EditableRow(
          dataSource,
          rowKey,
          this.formRef as unknown as IformRef,
        ),
        cell: EditableCell,
      },
    };
    const rowSelection = Object.assign(
      {
        type: 'checkbox',
        fixed: true,
      },
      this.props.rowSelection,
      { onChange: this.handleCheckChange },
    );
    return (
      <div className="editableTable">
        <Table
          {...this.props}
          rowSelection={this.props.isRowSelection ? rowSelection : undefined}
          components={components}
          dataSource={dataSource}
          columns={columns as ColumnTypes}
          title={this.getTitle}
        />
      </div>
    );
  }
}

EditableTable.defaultProps = {
  titlePosition: 'after',
  bordered: true,
  size: 'middle',
  pagination: false,
  dataSource: [],
  addBtn: true,
  deleteBtn: true,
  isRowSelection: true,
};
type IColumns = EditableTableProps['columns'];
export default EditableTable;
export type { editableRef, IColumns };
